#include "remotefunction.h"
#include "function.h"

#include "ecode.h"

#include <windows.h>

using namespace kog;

remote_function::remote_function(pipe& pipe)
: _pipe(&pipe)
, _is_servering(false)
{
	_service_guard.create_mutex(false);
	_map_guard.create_mutex(false);
	_call_guard.create_mutex(false);
	_cid_guard.create_mutex(false);

	_sem_service_queue.create_semaphore(0, MAX_SERVICE_QUEUE_SIZE);
}

remote_function::~remote_function(void)
{
	stop_service();
	_map_guard.wait();
	for (std::map<std::string, std::map<std::string, tr::ifunction_t*> >::iterator iter1 = _fmap.begin();
		iter1 != _fmap.end(); ++ iter1)
	{
		for (std::map<std::string, tr::ifunction_t*>::iterator iter2 = iter1->second.begin();
			iter2 != iter1->second.end(); ++ iter2)
		{
			delete iter2->second;
		}
	}
	_fmap.clear();
	_map_guard.release();

	_service_guard.close();
	_map_guard.close();
	_call_guard.close();
	_cid_guard.close();
	_sem_service_queue.close();
}

tr::function_proxy remote_function::operator[](const std::string& name)
{
	tr::function_proxy proxy(*this, name);
	return proxy;
}

void remote_function::start_service()
{
	int fid = 0;
	int tid = 0;
	int dtype = 0;
	pipe::msgpeice_t meta;
	int timeout = 500;
	_service_guard.wait();
	if (_is_servering)
	{
		_service_guard.release();
		return;
	}
	_is_servering = true;
	_service_guard.release();
	while (_is_servering)
	{
		int rcode = _pipe->recv(fid, tid, dtype, meta, timeout);
		if (rcode != 0)
		{
			// log info
			continue;
		}
		
		switch (dtype)
		{
		case fcrequest:
			_new_call(meta);
			break;
		case fcrmtcheck:
			_check_re(meta);
			break;
		case fcresponse:
			_result_v(meta);
			break;
		default:
			break;
		}
	}
	// wait for stop have finished
	_service_guard.wait();
	_service_guard.release();
}

// warning: use two locks in this function
void remote_function::stop_service()
{
	_service_guard.wait();
	_is_servering = false;

	_call_guard.wait();
	for (std::map<int, function_call*>::iterator iter = _fcalls.begin(); iter != _fcalls.end(); ++ iter)
	{
		iter->second->status = -1;
		iter->second->state = 5;
		iter->second->wevent.release();
	}
	_fcalls.clear();
	_call_guard.release();

	_service_guard.release();
}

void remote_function::_new_call(pipe::msgpeice_t meta)
{
	serial rs;
	unserial us(meta.ptr, meta.len);
	int cid = 0;
	int status = 0;
	std::string fname;
	std::string ftype;
	us.getvalue(cid);
	us.getvalue(fname);
	us.getvalue(ftype);

	_map_guard.wait();
	tr::ifunction_t* fun = _fmap[fname][ftype];
	_map_guard.release();

	// send response
	function_return fr;
	fr.cid = cid;
	fr.status = fun == NULL;
	pipe::msgpeice_t res_chk;
	res_chk.ptr = (const char*)&fr;
	res_chk.len = sizeof(fr);
	_pipe->send(0, 0, fcrmtcheck, &res_chk, 1);

	if (fun != NULL)
	{
		try {
			(*fun)(us, rs);
		} catch(...) {
			status = -1;
		}

		fr.status = status;
		std::string buf;
		rs.getbuffer(buf);
		pipe::msgpeice_t result[2] =
		{
			{(const char*)&fr, sizeof(fr)},
			{buf.c_str(), buf.size()}
		};
		_pipe->send(0, 0, fcresponse, result, 2);
	}
	if(meta.ptr != NULL) delete meta.ptr;
	printf("delete recv data.\n");
}

void remote_function::_check_re(pipe::msgpeice_t meta)
{
	function_return* pfr = (function_return*)meta.ptr;
	if (pfr == NULL) return;
	_call_guard.wait();
	std::map<int, function_call*>::iterator iter = _fcalls.find(pfr->cid);
	if (iter != _fcalls.end())
	{
		iter->second->state = 3;
		iter->second->status = pfr->status;
		// raise event
		iter->second->wevent.release();
	}
	else
	{
		// skip
	}
	_call_guard.release();

	delete []meta.ptr;
	printf("delete recv data.\n");
}

void remote_function::_result_v(pipe::msgpeice_t meta)
{
	function_return* pfr = (function_return*)meta.ptr;
	if (pfr == NULL) return;
	
	function_call* pcall = NULL;
	_call_guard.wait();
	std::map<int, function_call*>::iterator iter = _fcalls.find(pfr->cid);
	if (iter != _fcalls.end())
	{
		pcall = iter->second;
	}
	else
	{
		// skip
	}
	_call_guard.release();

	if (pcall != NULL)
	{
		pcall->status = pfr->status;
		pcall->wevent.release();
		pcall->msg = meta;
		pcall->state = 4;
	}
}

int remote_function::_next_cid()
{
	_cid_guard.wait();
	int cid = ++ _nextcid;
	if(_nextcid < 0)
	{
		_nextcid = 1;
	}
	_cid_guard.release();
	return cid;
}

int remote_function::_send_fcall(const char* data, int len)
{
	if (!_is_servering) return 0;
	std::auto_ptr<function_call> pcall(new function_call);
	printf("new function call.\n");
	pcall->cid = _next_cid();
	pcall->wevent.create_event(false, true);
	pcall->state = 2;
	memset(&pcall->msg, 0, sizeof(pcall->msg));
	pipe::msgpeice_t mv[2] = 
	{
		{(const char*)&pcall->cid, sizeof(pcall->cid)},
		{data, len},
	};

	int cid = pcall->cid;
	_call_guard.wait();
	_fcalls[cid] = pcall.release();
	_call_guard.release();

	if (0 != _pipe->send(0, 0, fcrequest, mv, 2))
	{
		printf("send failed!\n");
	}
	return cid;
}

// wait send done
int remote_function::_wait_send(int cid)
{
	if (!_is_servering) return KOG_ERROR_RF_STOP_SERVICE;
	int rcode = 0;
	_call_guard.wait();
	std::map<int, function_call*>::const_iterator iter = _fcalls.find(cid);
	if (iter == _fcalls.end())
	{
		rcode = KOG_ERROR_RF_CID_NOTFOUND;
	}
	_call_guard.release();
	return rcode;
}

int remote_function::_wait_response(int cid)
{
	if (!_is_servering) return KOG_ERROR_RF_STOP_SERVICE;
	int rcode = 0;
	function_call* pcall = NULL;
	_call_guard.wait();
	std::map<int, function_call*>::const_iterator iter = _fcalls.find(cid);
	if (iter == _fcalls.end())
	{
		rcode = KOG_ERROR_RF_CID_NOTFOUND;
	}
	else
	{
		pcall = iter->second;
	}
	_call_guard.release();
	
	if (rcode == 0)
	{
		while (_is_servering && pcall->state < 3)
		{
			while(_is_servering && 
				pcall->wevent.wait(500) == WAIT_TIMEOUT);
		}

		if (!_is_servering)
		{
			rcode = KOG_ERROR_RF_STOP_SERVICE;
		}
		else if (pcall->state >= 3 && (rcode = pcall->status) != 0)
		{
			// if is not servering now, stop service will clear all data
			_call_guard.wait();
			_fcalls.erase(cid);
			_call_guard.release();

			printf("delete function call.\n");

			// no need pcall
			if (pcall->msg.ptr != NULL) 
			{
				delete [](char*)pcall->msg.ptr;
				printf("delete data.\n");
				printf("delete recv data.\n");
			}
			pcall->wevent.close();
			delete pcall;
		}
	}

	return rcode;
}

int remote_function::_wait_result(int cid, char*& v, int& len)
{
	int rcode = 0;
	function_call* pcall = NULL;
	_call_guard.wait();
	std::map<int, function_call*>::iterator iter = _fcalls.find(cid);
	if (iter == _fcalls.end())
	{
		rcode = KOG_ERROR_RF_CID_NOTFOUND;
	}
	else
	{
		pcall = iter->second;
	}
	_call_guard.release();
	
	if (rcode == 0)
	{
		while (_is_servering && pcall->state < 4)
		{
			while(_is_servering && 
				pcall->wevent.wait(500) == WAIT_TIMEOUT);
		}

		pcall->wevent.close(); // close event handle
		if (!_is_servering)
		{
			rcode = KOG_ERROR_RF_STOP_SERVICE;
		}
		else if (pcall->state >= 4)
		{
			rcode = pcall->status;
			v = (char*)pcall->msg.ptr;
			len = pcall->msg.len;

			// erase it from map
			_call_guard.wait();
			_fcalls.erase(cid);
			_call_guard.release();
		}

		// delete it
		delete pcall;
		printf("delete function call.\n");
	}
	return rcode;
}
